﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Data.Schema.Build;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.SchemaModel;
using System.Globalization;
using Microsoft.Data.Schema.ScriptDom;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.Sql.Build;
using Microsoft.Data.Schema.Sql.SchemaModel;
using System.IO;



namespace Inhouse.VisualStudio.Addins.LinkedServerContributor
{
	/// <summary>
	/// This deployment contributor modifies a deployment plan by adding droplogins to sp_dropserver calls.
	/// </summary>
	[DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
	public class LinkedServerContributor : DeploymentPlanModifier
	{
		
		/// <summary>
		/// You override the OnExecute method to do the real work of the contributor.
		/// </summary>
		/// <param name="context"></param>
		protected override void OnExecute(DeploymentPlanContributorContext context)
		{
			
			
			// Obtain the first step in the Plan from the provided context
			DeploymentStep nextStep = context.PlanHandle.Head;
			bool mainBody = false;
			// Loop through all steps in the deployment plan
			while (nextStep != null)
			{
				// Increment the step pointer, saving both the current and next steps
				DeploymentStep currentStep = nextStep;
				nextStep = currentStep.Next;

				if (currentStep is BeginPostDeploymentScriptStep)
				{
					// This is the step that marks the beginning of the post deployment script.  
					// We do not continue processing after this point.
					mainBody = false;
					break;
				}

				if (currentStep is BeginPreDeploymentScriptStep)
				{
					mainBody = false;
					continue;
				}

				if (currentStep is EndPreDeploymentScriptStep)
				{
					mainBody = true;
					continue;
				}

				//If the currentStep is dropping something and we are in the mainBody of the script then
				//the element might be responsible for dropping a linked server.
				if (currentStep is DropElementStep && mainBody)
				{
					AlterLinkedServerStatement((DropElementStep)currentStep);	
				}
			}

		}

		//This method determines if a the drop statement targets a linked server.
		//If it does then then @droplogins is appended to the list of parameters for the SP
		private static void AlterLinkedServerStatement(DropElementStep currentStep)
		{

			if (currentStep.TargetElement != null && currentStep.TargetElement is ISqlLinkedServer)
			{
				ExecuteStatement addLinkedServerSP;

				//Check we can cast to TSqlScript and this statement only has one batch, anything else is an error
				if (currentStep.Script is TSqlScript && (currentStep.Script as TSqlScript).Batches.Count == 1)
				{
					//Check that the first statement exists and it is an execute SP statement
					if ((currentStep.Script as TSqlScript).Batches[0].Statements.Count >= 1
						&& (currentStep.Script as TSqlScript).Batches[0].Statements[0] is ExecuteStatement)
					{
						//We have a linked server so this code is appending the @droplogins parameter to the SP call.
						addLinkedServerSP = (ExecuteStatement)(currentStep.Script as TSqlScript).Batches[0].Statements[0];
						ExecuteParameter dropLogins = new ExecuteParameter();
						dropLogins.Variable = CreateLiteral("@droplogins", LiteralType.Variable);
						dropLogins.ParameterValue = CreateLiteral("droplogins", LiteralType.AsciiStringLiteral);
						addLinkedServerSP.ExecutableEntity.Parameters.Add(dropLogins);
					}
				}
				
				

			}
		}

		private static Literal CreateLiteral(string value, LiteralType type)
		{
			Literal l = new Literal();
			l.Value = value;
			l.LiteralType = type;
			return l;
		}
		
	}
}
